﻿using System;
using System.Collections.Generic;
using SSG.Tools;

namespace SSG.Model
{
    /// <summary>
    /// SID / STAR
    /// </summary>
    public abstract class SidStar : Procedure
    {
        private readonly List<RunwayTrack> _runwayTracks = new List<RunwayTrack>();

        /// <summary>
        /// Corresponding runway tracks
        /// </summary>
        public List<RunwayTrack> RunwayTracks { get { return this._runwayTracks; } }

        /// <summary>
        /// Fixesof runway tracks
        /// </summary>
        public List<Fix> RunwayTracksFixes
        {
            get
            {
                List<Fix> fixes = new List<Fix>();
                if (this._runwayTracks.NullSafeIsEmpty()) return fixes;
                foreach (RunwayTrack tr in this._runwayTracks) {
                    fixes.AddRange(tr.Fixes);
                }
                return fixes;
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name"></param>
        /// <param name="airport"></param>
        /// <param name="runway"></param>
        protected SidStar(string name, Airport airport, Runway runway) : base(name, airport, runway)
        {
            // code
        }

        /// <summary>
        /// Add a runway track
        /// </summary>
        /// <param name="rwt"></param>
        public void AddRunwayTrack(RunwayTrack rwt)
        {
            if (rwt == null) throw new ArgumentNullException("rwt");
            this.RunwayTracks.Add(rwt);
            this.AddRunways(rwt.Runways);
        }

        /// <summary>
        /// Parse the SID STAR block
        /// </summary>
        /// <param name="sidStarBlock"></param>
        public void FromPmdgSidStarBlock(string[] sidStarBlock)
        {
            if (sidStarBlock.NullSafeIsEmpty()) return;
            foreach (string cl in sidStarBlock) {
                if (cl.StartsWith("  RNW")) {
                    // Note 2 spaces
                    this.AddCorrespondingRunwaysFromPmdgLine(cl);
                }
                else if (cl.StartsWith(" RNW")) {
                    // note single space
                    RunwayTrack rt = Procedure.FromPmdgFormat(cl, this.Airport) as RunwayTrack;
                    if (rt == null) throw new InvalidOperationException("Cannot parse line " + cl);
                    this.AddRunwayTrack(rt);
                }
                else if (cl.StartsWith(" TRANSITION")) {
                    Transition t = Procedure.FromPmdgFormat(cl, this.Airport) as Transition;
                    if (t == null) throw new InvalidOperationException("Cannot parse line " + cl);
                    this.AddTransition(t);
                }
            }
        }

        /// <summary>
        /// Add a runway line: "  RNW 10 RNW 28" 
        /// </summary>
        /// <param name="rwyLine"></param>
        private void AddCorrespondingRunwaysFromPmdgLine(string rwyLine)
        {
            if (string.IsNullOrEmpty(rwyLine)) throw new ArgumentNullException("rwyLine");
            if (this.Airport == null) throw new InvalidOperationException("Missing airport for " + this);
            if (this.Airport.Runways.NullSafeIsEmpty()) throw new InvalidOperationException("No runways " + this);

            // "  RNW 10 RNW 28"
            List<Runway> rws = this.Airport.GetRunways(Runway.RunwayNamesFromPmdgLine(rwyLine));
            if (!rws.NullSafeIsEmpty()) this.AddRunways(rws);
        }

        /// <summary>
        /// Fixes with runway tracks
        /// </summary>
        /// <param name="runway"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public override List<Fix> FixesWithRunwayTracks(Runway runway, GeneratorSettings settings)
        {
            return runway == null ? this.Fixes : this.FixesWithRunwayTracks(runway.Name, settings);
        }

        /// <summary>
        /// Fixes with runway tracks
        /// </summary>
        /// <param name="runway"></param>
        /// <param name="settings"></param>
        /// <returns></returns>
        public override List<Fix> FixesWithRunwayTracks(string runway, GeneratorSettings settings)
        {
            if (settings == null) settings = GeneratorSettings.DefaultSettings;
            if (settings == null) throw new InvalidOperationException("No settings");
            bool useRunwayTracks = settings.UseRunwayTracks(this.GetType());
            if (String.IsNullOrEmpty(runway) || !useRunwayTracks || this._runwayTracks.NullSafeIsEmpty()) return this.Fixes;
            List<Fix> rwtFixes = new List<Fix>();
            foreach (RunwayTrack rwt in this._runwayTracks) {
                if (rwt.IsValidForRunway(runway)) rwtFixes.AddRange(rwt.Fixes);
            }
            if (this is Sid) {
                // Runway fixes first
                rwtFixes.AddRange(this.Fixes);
                return rwtFixes;
            }

            if (this is Star) {
                // Runway fixes last
                rwtFixes.InsertRange(0, this.Fixes);
                return rwtFixes;
            }

            // TODO: Further decisions wether to insert / append?
            rwtFixes.AddRange(this.Fixes);
            return rwtFixes;
        }
    }
}